home *** CD-ROM | disk | FTP | other *** search
- /*
- Author: Andrew Lowry
-
- Columbia University Center for Computing Activities, July 1986.
- Copyright (C) 1986, 1987, Trustees of Columbia University in the City
- of New York. Permission is granted to any individual or institution
- to use, copy, or redistribute this software so long as it is not sold
- for profit, provided this copyright notice is retained.
- */
- /* TODO: Finish dtdst, figure out why always GMT */
-
- /* datime
- **
- ** Utilities to deal with times and dates. Two routines are machine-
- ** specific and must be written for each machine individually. These
- ** are dtnow, which fills in a datime structure with the current
- ** date and time, and dttzone, which returns the local timezone in
- ** minutes west of Greenwich and a timezone id, if available.
- **
- ** Required structure and constant declarations are to be found in datime.h.
- ** tzone.h contains declarations of timezone information, separated from
- ** this source for ease in modifying the rules.
- **/
-
- #include "ccmdlib.h"
- #include "tzone.h" /* declare timezone info structures */
- #include "dtpat.h" /* get date patterns */
-
- /* A few useful character constants */
-
- #define NULCHAR '\000'
- #define TAB '\011'
- #define SPACE '\040'
-
-
-
- /* dtnow
- **
- ** Purpose:
- ** Return, via a datime structure supplied by the caller, the current
- ** date and time, using local timezone information.
- **
- ** Input arguments:
- ** dtblk - Pointer to a datime block to be filled in.
- **
- ** Output arguments:
- ** dtblk - Fields filled in with current info.
- **
- ** Returns: Nothing.
- **/
-
- #ifdef MSDOS
- /* #include <dos.h> */
-
- /* macro to convert from 2-digit BCD to integer */
-
- #define bcd2int(x) (((x)&0xf)+(10*(((x)>>4)&0xf)))
-
- dtnow(dtblk)
- datime *dtblk;
- {
- union REGS inregs, outregs; /* structures for communicating with BIOS */
- int junk;
-
- inregs.h.ah = 2; /* read the time */
- int86(0x1a,&inregs,&outregs);
- dtblk->_dthr = bcd2int(outregs.h.ch); /* transfer values */
- dtblk->_dtmin = bcd2int(outregs.h.cl);
- dtblk->_dtsec = bcd2int(outregs.h.dh);
-
- inregs.h.ah = 4; /* read the date */
- int86(0x1a,&inregs,&outregs);
- dtblk->_dtmon = bcd2int(outregs.h.dh)-1; /* transfer values */
- dtblk->_dtday = bcd2int(outregs.h.dl)-1;
- dtblk->_dtyr = bcd2int(outregs.h.cl)+100*bcd2int(outregs.h.ch);
- /* compute day-of-week */
- dtblk->_dtdow = dtdow(dtblk->_dtmon,dtblk->_dtday,dtblk->_dtyr);
- dttzone(&dtblk->_dttz,&dtblk->_dttzc); /* get local timezone info */
- dtblk->_dtdst = dtdst(dtblk); /* compute daylight savings time */
- }
- #endif
-
- #if unix
-
- dtnow(dtblk)
- datime *dtblk;
- {
- long t;
- struct tm *localtime(), *x;
- t = time(NULL); /* get time */
- x = localtime(&t); /* convert it */
- dtblk->_dthr = x->tm_hour; /* hour */
- dtblk->_dtmin = x->tm_min; /* minute */
- dtblk->_dtsec = x->tm_sec; /* second */
- dtblk->_dtmon = x->tm_mon; /* month */
- dtblk->_dtday = x->tm_mday -1; /* day of month */
- dtblk->_dtyr = x->tm_year+1900; /* year */
- dtblk->_dtdow = x->tm_wday; /* week day */
- dttzone(&dtblk->_dttz,&dtblk->_dttzc);/* time zone */
- dtblk->_dtdst = dtdst(dtblk); /* daylight savings time */
- }
- #endif /* unix */
-
-
- /* dttzone
- **
- ** Purpose:
- ** Returns the local timezone, as minutes west of Greenwich. Daylight
- ** savings time is not considered. If a timezone ID is known, it is
- ** returned as well.
- **
- ** Input arguments: None.
- ** Output arguments:
- ** offset - Number of minutes west of Greenwich, in range -7199 to 7200
- ** (that is, the international dateline is considered 12 hours WEST,
- ** not 12 hours EAST of Greenwich)
- ** code - Timezone code number if known, or -1
- **
- ** Returns: Nothing
- **/
-
- #if MSDOS
- dttzone(offset,code)
- int *offset,*code;
- {
- *offset = LCL_TZOFF; /* get offset defined in datime.h */
- #ifdef LCL_TZCODE
- *code = LCL_TZCODE; /* and code from datime.h if there is one */
- #else
- *code = -1; /* otherwise signal no code */
- #endif
- }
- #endif
-
- #if (BSD)
- dttzone(offset, code)
- int *offset, *code;
- {
- struct timeval tp;
- struct timezone tz;
- gettimeofday(&tp,&tz); /* get time of day */
- *offset = tz.tz_minuteswest;
- #ifdef LCL_TZCODE
- *code = LCL_TZCODE; /* and code from datime.h if there is one */
- #else
- *code = -1; /* otherwise signal no code */
- #endif
- }
- #endif /* BSD */
-
- #ifdef SYSV
- dttzone(offset, code)
- int *offset, *code;
- {
- *offset = timezone / 60; /* get the correct timezone */
- #ifdef LCL_TZCODE
- *code = LCL_TZCODE; /* and code from datime.h if there is one */
- #else
- *code = -1; /* otherwise signal no code */
- #endif
- }
- #endif /* SYSV */
-
-
-
-
- /* dttzinf
- **
- ** Purpose:
- ** Return a pointer to a timezone information structure, given
- ** a timezone code or an offset from Greenwich. The former style
- ** of call is identified by the input argument being a positive
- ** value with flag TZ_CODE set. Anything else is taken to be
- ** a signed number of minutes west of Greenwich. In the former
- ** case, the tzinfo array is scanned for a timezone with the given
- ** code, and a pointer to the corresponding tzinf structure is
- ** returned. When an offset is given, the first timezone with
- ** the given offset is returned (so timezones with different
- ** dst rules, or in different hemispheres, cannot be distinguished
- ** by means of their offsets). In either case, if no matching
- ** timezone is located, NULL is returned.
- **
- ** Input arguments:
- ** key - Signed offset in minutes west of Greenwich, or positive
- ** timezone code value with the TZ_CODE flag turned on.
- **
- ** Output arguments: None.
- ** Returns: Pointer to first matching tzinf structure, or NULL if none found.
- **/
-
- tzinf *
- dttzinf(key)
- int key;
- {
- int bycode; /* TRUE if looking up by code */
- int i;
-
- if ((key > 0) && (key & TZ_CODE)) { /* lookup by code? */
- bycode = TRUE; /* set flag */
- key &= ~TZ_CODE; /* and isolate the code value */
- }
- else
- bycode = FALSE; /* otherwise clear flag */
-
- for (i = 0; i < TZCOUNT; i++) /* loop through the table */
- if (key == (bycode ? tzinfo[i]._tzcode : tzinfo[i]._tzoff))
- return(&tzinfo[i]); /* found indicated zone */
- return(NULL); /* zone not in table */
- }
-
-
-
- /* dtdow
- **
- ** Purpose:
- ** Given a date, compute the day of week on which the date falls,
- ** according to the Gregorian calendar system. The calculation
- ** assumes that the Gregorian system has been in effect since
- ** 1-Jan-0000, which is not true. In general, correct results
- ** will be given for dates after 1923, by which time all the
- ** major cultures had adopted the calendar. See a world almanac
- ** or other source for details of who joined the system when.
- **
- ** Input arguments:
- ** mon - The month for the date in question, 0 = January
- ** day - The day of the month, with 0 = the first day.
- ** yr - The year in question, with no conversion for years
- ** less than 100.
- **
- ** Output arguments: None.
- ** Returns: Day of week for given date, with 0 = Sunday, 6 = Saturday.
- **/
-
- /* Useful macro to decide if a year is a leap year */
-
- #define LEAP(y) (((y)%400 == 0) || (((y)%100 != 0) && ((y)%4 == 0)))
-
- /* lengths of the months, assuming a leap year */
- static int mthlens[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
- int
- dtdow(mon,day,yr)
- int mon,day,yr;
- {
- int dow; /* calculated day of week */
- int i;
-
- /* First calculate day-of-week relative to 1-Jan-0000, that is,
- ** the number of days since that date, modulo 7. A first approximation
- ** accounts for years prior to the given year, by calculating (365*yr) % 7.
- ** But since 365%7 is 1, this is the same as (1*yr) % 7, or yr % 7.
- ** Adjustments are then made for leap years, and then for preceding months
- ** in the given year, and preceding days in the given month.
- **/
- dow = yr % 7; /* start with days in prior years */
- dow += (yr+3) / 4; /* adjust for preceding leap years */
- dow -= (yr+99) / 100; /* most centuries are non-leap */
- dow += (yr+399) / 400; /* but every fourth one is */
- for (i = 0; i < mon; i++)
- dow += mthlens[i]; /* add preceding months this year */
- if ((mon > 1) && !LEAP(yr)) /* after non-leap Feb? */
- dow -= 1; /* yup, we added to much */
- dow += day; /* now adjust for date within month */
- dow += 6; /* Greg 1-Jan-0000 was a Saturday */
- dow %= 7; /* get dow in range 0-7 */
- return(dow); /* give result to caller */
- }
-
-
-
- /* dtdst
- **
- ** Purpose:
- ** Given a datime structure filled in with date, time, and timezone
- ** information, compute the daylight-savings-time adjustment that
- ** should be in effect according to whatever rules we have for the
- ** given timezone. If no appropriate rule exists, assume no adjustment
- ** (that is, an adjustment of zero). Generally, when dst is in effect,
- ** the adjustment will be -60, meaning that to convert to standard time,
- ** 60 minutes should be subtracted from the given time. If a timezone
- ** code is present in the datime structure, it is used to find the dst
- ** rules. Otherwise, the timezone offset is used. In the latter case,
- ** the wrong rules may be applied since timezones with identical offsets
- ** but different dst rules will not be distinguished.
- **
- ** Input arguments:
- ** dtblk - A pointer to the datime block holding the date, time, and zone
- ** information to use.
- **
- ** Output arguments: None. (In particular, the _dtdst field of the given
- ** datime block is NOT modified.)
- **
- ** Returns: Number of minutes to add to given time in order to convert
- ** to standard time in its timezone (so adding this value as well
- ** as the _dttz value will result in Greenwich Mean Time).
- **
- **/
-
- int
- dtdst(dtblk)
- datime *dtblk;
- {
- tzinf *tzi; /* time zone info block */
- dstrule *rule; /* applicable dst rule */
- int julian, dston, dstoff, i;
-
- if (dtblk->_dttzc != -1)
- tzi = dttzinf(TZ_CODE | dtblk->_dttzc); /* look up by code if set */
- else
- tzi = dttzinf(dtblk->_dttz); /* otherwise look up by offset */
-
- if (tzi == NULL)
- return(0); /* no adjustment if tz not known */
-
- /* calculate julian date of current date */
- for (julian = dtblk->_dtday+1, i = 0; i < dtblk->_dtmon; i++)
- julian += mthlens[i];
- if ((dtblk->_dtmon > 1) && !LEAP(dtblk->_dtyr))
- julian--;
-
- for (rule = tzi->_tzdst; ; rule++) {
- if (dtblk->_dtyr < rule->_dsyr1 || dtblk->_dtyr > rule->_dsyrn) {
- if (rule->_dsyr1 > 0)
- continue;
- else return 0;
- }
-
- /* get appropximate dates of dst on/off */
- dston = rule->_dsdow; /* XXX struct member is mislabeled */
- dstoff = rule->_dsday; /* XXX struct member is mislabeled */
-
- /* account for feb 29th */
- if (!LEAP(dtblk->_dtyr)) {
- if (dston >= 59)
- dston--, dstoff--;
- else
- if (dstoff >= 59)
- dstoff--;
- }
-
- /*
- * get actual julian dates for dst on/off, assumed to be Sunday
- * previous to the approximate date.
- */
-
- /* get days from Jan 1st to first Sunday */
- i = (julian - 1 + 7 - dtblk->_dtdow) % 7;
- /* subtract number of days between dston and previous Sunday */
- dston -= ((((dston - 1) % 7) - i) + 7) % 7;
- dstoff -= ((((dstoff - 1) % 7) - i) + 7) % 7;
-
-
- if ((julian < dston) || (julian > dstoff) ||
- (julian == dston && (dtblk->_dthr < 2)) ||
- (julian == dstoff && (dtblk->_dthr >= (tzi->_tzsth ? 2 : 1)))) {
- if (rule->_dsyr1)
- continue;
- else return (0);
- }
- return (- tzi->_tzdadj);
- }
- }
-
-
-
- /* dtparse
- **
- ** Purpose:
- ** Attempt to interpret a given input string as a date/time specification.
- ** If successful, fill the results into a given datime block. A successful
- ** parse need not consume all the available input.
- **
- ** The input must match one of the patterns declared in dtpat.h, which
- ** cover a wide range of formats. Flags may be passed to constrain
- ** which patterns may be used, or to indicate that either the time or
- ** the date portion is not to be parsed. The patterns are ranked so
- ** that a pattern of a higher rank will be chosen when there is
- ** ambiguity in the input. The ranking can be supplemented by a set of
- ** adjustments that depend on the flags that are specified in the parse,
- ** so that the ranking can depend on the flags. For details of how
- ** the patterns are constructed, read the comments in dtpat.h.
- **
- ** Parsing succeeds if, of all the patterns matching the current input,
- ** there is a single matching pattern of highest rank. The input is
- ** decoded according to that pattern, checked for acceptability, and
- ** then returned via the datime structure supplied by the caller.
- **
- ** Completion text is set by some pattern elements when they encounter
- ** the end of the input.
- **
- ** Input arguments:
- ** flags - Flags to control the parse, as defined in datime.h
- ** text - A pointer to the text to be parsed.
- ** textlen - The number of characters in the text to be parsed.
- **
- ** Output arguments:
- ** dtblk - Filled in with date/time info from parse if successful.
- ** If time is not given, 00:00:01 is used, with the local timezone
- ** and daylight savings computed by the rules. If date is not
- ** given, the current date is filled in. Day of week is computed
- ** from the date, and if the input specifies a day of week, it
- ** must match the computed day in order to succeed.
- ** parselen - The number of characters consumed by a successful parse.
- ** completion - A pointer to text that can be used to complete
- ** the current input (generally only partial completion results)
- ** if the date/time specification is incomplete.
- ** incomplete - TRUE iff the parse failed due to lack of sufficient
- ** input.
- **
- ** Returns: TRUE iff the parse succeeds.
- **/
-
- /* Define TRACE here if a pattern match trace is desired */
-
- /* #define TRACE */
-
- /* Global variables used by the pattern matching routines */
-
- static char *dttext; /* pointer to input text */
- static int dtlen; /* # of chars left to parse */
- static int dtinc; /* parse failed for lack of text */
- static char *dtcmp; /* pointer to completion text after */
- /* incomplete parse */
-
- int
- dtparse(flags,text,textlen,dtblk,parselen,completion,incomplete)
- int flags,textlen,*parselen,*incomplete;
- char *text,**completion;
- datime *dtblk;
- {
- datime curdt; /* current time/date go here */
- dtpat *pats; /* pattern array selected by flags */
-
- dttext = text; /* set up global variables */
- dtlen = textlen;
- dtinc = *incomplete = FALSE; /* assume not incomplete */
-
- if (flags & DTP_NDA) /* check exclusions from parse */
- if (flags & DTP_NTI)
- return(FALSE); /* cannot exlude both time and date */
- else
- pats = timpats; /* exclude date but not time */
- else
- if (flags & DTP_NTI)
- pats = datpats; /* exclude time but not date */
- else
- pats = dtpats; /* exclude neither */
- if (!match(pats,flags)) { /* attempt a parse */
- if (dtinc) { /* reached end of input? */
- *incomplete = TRUE; /* yup, let them know */
- *completion = dtcmp; /* and pass on completion text */
- }
- return(FALSE); /* give a failure return */
- }
- if (flags & DTP_NDA) { /* need current date info? */
- dtnow(&curdt); /* get it */
- dtblk->_dtmon = curdt._dtmon; /* and transfer date info */
- dtblk->_dtday = curdt._dtday;
- dtblk->_dtyr = curdt._dtyr;
- dtblk->_dtdow = curdt._dtdow;
- }
- else {
- dtblk->_dtmon = mon; /* otherwise transfer parsed values */
- dtblk->_dtday = day;
- dtblk->_dtyr = yr;
- dtblk->_dtdow = dow;
- }
-
- if (flags & DTP_NTI) { /* time not parsed? */
- dtblk->_dthr = 0; /* fill in one second past midnight */
- dtblk->_dtmin = 0;
- dtblk->_dtsec = 1;
- dttzone(&dtblk->_dttz,&dtblk->_dttzc); /* get local timezone info */
- dstspec = -1; /* need dst computation */
- }
- else { /* parsed time - fill in values */
- dtblk->_dthr = hr;
- dtblk->_dtmin = min;
- dtblk->_dtsec = sec;
- if (tzspec != -1) { /* timezone given? */
- dtblk->_dttz = tz; /* then transfer it */
- dtblk->_dttzc = tzc;
- }
- else {
- dttzone(&dtblk->_dttz,&dtblk->_dttzc); /* otherwise get local zone */
- dstspec = -1; /* and no dst indicator given */
- }
- }
- if (dstspec == -1) /* dst indicator not given? */
- dtblk->_dtdst = dtdst(dtblk); /* then compute it by the rules */
- else
- dtblk->_dtdst = dst; /* else set value from parse */
-
- *parselen = dttext - text; /* set # of chars consumed */
-
- return(TRUE); /* and succeed */
- }
-
-
- /* The pattern matcher routines */
-
- #define MAXVAL 2
- static int patval[MAXVAL]; /* values set by pattern elements */
-
- /* match
- **
- ** Purpose:
- ** Apply a given set of patterns to the text currently pointed to by
- ** global variable 'dttext'. If there is a single succeeding pattern
- ** of highest rank, consume the characters and return TRUE. Otherwise
- ** return FALSE. As soon as a highest-rank failing pattern sets the
- ** dtinc flag, a FALSE return is made immediately without attempting
- ** other patterns. On success, the index of the succeeding pattern in
- ** the pattern array is returned via the patval array.
- **
- ** Input arguments:
- ** pats - Pointer to the first pattern in an array of patterns to attempt,
- ** terminated by a pattern whose pattern string is NULL.
- ** flags - The flags specified in the FDB for the parse.
- **
- ** Output arguments: None
- ** Returns: TRUE if matching succeeds, FALSE otherwise.
- **
- ** Global variables:
- ** dttext - Pointer to the text to be matched, updated after a successful
- ** match
- ** dtlen - Number of characters in input text, updated after success
- ** dtinc - TRUE after a failing match that failed because of insufficient
- ** input text.
- ** dtcmp - After an incomplete match, points to appropriate completion
- ** text, or NULL if there is none.
- **/
-
-
- #ifdef TRACE
- static int tracelev = 0; /* indentation level for trace */
- #endif
-
- static int
- match(pats,flags)
- dtpat *pats;
- int flags;
- {
- #ifdef TRACE
- int ret; /* results of match operation */
- tracelev +=2;
- trindent();fprintf(stderr,"Matching: ");trshow();trnl();
- tracelev +=2;
- ret = match1(pats,flags);
- tracelev -= 2;
- trindent();fprintf(stderr,"Match result: %s\n",(ret ? "SUCCESS":"FAILURE"));
- tracelev -= 2;
- return(ret);
- }
- static int
- match1(pats,flags)
- dtpat *pats;
- int flags;
- {
- #endif
-
- char *origtext = dttext; /* save input parameters */
- int origlen = dtlen; /* to be restored on failure */
- char *savtext; /* input params after a good match */
- int savlen;
- int rank; /* ranks of individual patterns */
- int hirank; /* highest pattern rank */
- dtpat *p; /* for stepping through patterns */
- int matched = FALSE; /* TRUE if a pattern succeeds */
-
- hirank = -1; /* assume no patterns eligible */
- for (p = pats; p->_dppat != NULL; p++) { /* step through the patterns */
- rank = patrank(p,flags); /* compute ranks */
- if (rank > hirank)
- hirank = rank; /* and remember highest rank */
- }
-
- while (hirank >= 0) { /* step down through eligible ranks */
- for (p = pats; p->_dppat != NULL; p++) /* loop through pat at that rank */
- if (patrank(p,flags) == hirank) {
- if (matchpat(p,flags)) /* good match? */
- #ifdef TRACE
- { tracelev -= 2; trindent(); fprintf(stderr,"PATTERN SUCCEEDED\n");
- #endif
- if (!matched) { /* first one? */
- matched = TRUE; /* then say we matched */
- savtext = dttext; /* save the input parameters */
- savlen = dtlen;
- set1val(p - pats); /* set index of good pattern for */
- /* return value for %r call */
- }
- else
- return(FALSE); /* multiple highest-rank matches */
- #ifdef TRACE
- }
- #endif
- else if (dtinc) /* incomplete match? */
- #ifdef TRACE
- { tracelev -= 2; trindent(); fprintf(stderr,"PATTERN FAILED (INC)\n");
- #endif
- return(FALSE); /* then quit now */
- #ifdef TRACE
- }
- else {
- tracelev -= 2; trindent(); fprintf(stderr,"PATTERN FAILED\n");
- }
- #endif
-
- dttext = origtext; /* restore input for next pattern */
- dtlen = origlen;
- }
- if (matched) { /* single match at this rank? */
- dttext = savtext; /* fix global vars to consume input */
- dtlen = savlen;
- return(TRUE); /* and succeed */
- }
- else
- hirank--; /* no match, try next lower rank */
- }
-
- return(FALSE); /* no success at any rank */
- }
-
-
-
- /* patrank
- **
- ** Purpose:
- ** Compute the rank for a parse pattern with a given set of flags.
- ** If any of the flags disqualifies the pattern (via its _dpqal field),
- ** -1 is returned. Otherwise, rank adjustments are applied, to the
- ** base rank, and the adjusted rank is returned.
- **
- ** Input args:
- ** pat - A pointer to the pattern to be ranked.
- ** flags - Parse flags active for the parse.
- **
- ** Output args: None
- ** Returns: Adjusted rank.
- **/
-
- static int
- patrank(pat,flags)
- dtpat *pat;
- int flags;
- {
- int rank; /* base rank for the pattern */
- rankadj *adj; /* pointer to adjustment array */
-
- if (flags & pat->_dpqal) /* any disqualifying flags active? */
- return(-1); /* yup, throw it out */
-
- rank = pat->_dprnk; /* get the base rank */
- adj = pat->_dpraj; /* and point to adjustments array */
- if (adj != NULL)
- while (adj->_raflg != -1) { /* loop through adjustments */
- if (adj->_raflg & flags) /* adjustment indicated by flags? */
- rank += adj->_raadj; /* yup, adjust the rank */
- adj++; /* and move to next adjustment */
- }
- return(rank); /* give back adjusted rank */
- }
-
-
-
- /* matchpat
- **
- ** Purpose:
- ** Apply a given pattern to the current input. Return TRUE if all the
- ** required special pattern elements succeed and if intervening pattern
- ** characters match the input. Otherwise return FALSE. After a success,
- ** the global input parameters (dttext and dtlen) will be left so as to
- ** consume the input characters that participated in the match. On
- ** failure caused by insufficient input text, global variable dtinc
- ** will be set TRUE, and dtcmp may point to completion text to be
- ** filled in if completion is requested.
- **
- ** Input args:
- ** pat - A pointer to the pattern to be matched.
- ** flags - The flags in effect for the parse.
- **
- ** Output args: None
- ** Returns: TRUE for a successful match, FALSE for a failure.
- **/
-
- static int
- matchpat(pat,flags)
- dtpat *pat;
- int flags;
- {
- int **patarg = pat->_dparg; /* point to pattern args */
- char *pats = pat->_dppat; /* and to pattern string */
- char pc,tc; /* chars from pat & dttext input */
- char *opttext; /* vars to save input state */
- int optlen; /* before an optional pat element */
- int opt; /* TRUE iff during optional pat elt */
- int nogood; /* TRUE after a failing pattern elt */
- char *delims; /* pointer to a %d arg string */
- int i;
-
- #ifdef TRACE
- trindent();fprintf(stderr,"Pattern [%s] Input [",pats);
- trshow();fprintf(stderr,"]\n");
- tracelev += 2;
- #endif
-
- clearval(); /* clear the pattern values */
-
- while ((pc = *pats++) != NULCHAR) { /* scan through pattern string */
- if (pc == SPACE)
- continue; /* skip spaces in pattern */
- if (pc != '%') { /* normal character */
- #ifdef TRACE
- trindent();fprintf(stderr,"Char [%c] ",pc);
- #endif
- if (dtlen == 0) { /* reached end of input */
- dtinc = TRUE; /* signal incomplete match */
- dtcmp = NULL; /* no completion on normal chars */
- #ifdef TRACE
- fprintf(stderr,"INC\n");
- #endif
- return(FALSE); /* and fail */
- }
- tc = *dttext++; /* get next input char */
- dtlen--; /* and consume it */
- if (islower(tc)) /* convert to upper case */
- tc = toupper(tc);
- if (islower(pc))
- pc = toupper(pc);
- if (pc != tc)
- #ifdef TRACE
- { fprintf(stderr,"NO\n");
- #endif
- return(FALSE); /* fail if chars do not match */
- #ifdef TRACE
- }
- else
- fprintf(stderr,"OK\n");
- #endif
- continue; /* otherwise continue scanning */
- }
- pc = *pats++; /* get special element char */
- #ifdef TRACE
- trindent();
- #endif
- if (pc == '?') { /* this element optional? */
- #ifdef TRACE
- fprintf(stderr,"Optional ");
- #endif
- opt = TRUE; /* flag it */
- opttext = dttext; /* and save input state */
- optlen = dtlen;
- pc = *pats++; /* and get next pattern char */
- }
- else
- opt = FALSE; /* else clear optional flag */
- #ifdef TRACE
- fprintf(stderr,"Special [%c] Args [",pc); trargs();
- fprintf(stderr,"] Input [");trshow();
- fprintf(stderr,"] ");
- #endif
- nogood = FALSE; /* assume pat element will succeed */
-
- switch(pc) { /* now dispatch to element handler */
- case '=': /* assign value from prior element */
- **(patarg++) = patval[0]; /* assign value, move to next arg */
- for (i = 1; i < MAXVAL; i++) /* cycle the pattern value array */
- patval[i-1] = patval[i];
- patval[MAXVAL-1] = -1; /* and clear the last value */
- break;
-
- case '%': /* match a percent sign */
- if (dtlen-- == 0) /* insufficient input? */
- dtinc = nogood = TRUE; /* yes, flag incomplete failure */
- else if ((*dttext++) != '%')
- nogood = TRUE; /* check for next char match */
- else
- set1val(0); /* returns 0 on success */
- break;
-
- case SPACE: /* match a space */
- if (dtlen-- == 0) /* insufficient input? */
- dtinc = nogood = TRUE; /* yes, flag incomplete failure */
- else if ((*dttext++) != SPACE)
- nogood = TRUE; /* next char not a space */
- else
- set1val(0); /* good match */
- break;
-
- case 'c': /* match next pattern char */
- case 'C':
- #ifdef TRACE
- fprintf(stderr,"Char [%c] ",*(pats+1));
- #endif
- if (dtlen-- == 0) /* insufficient input? */
- dtinc = nogood = TRUE; /* yes, flag incomplete failure */
- else if ((*dttext++) != *pats++)
- nogood = TRUE; /* next char does not match */
- else
- set1val(0); /* good match */
- break;
-
- case 'k': /* keyword match */
- case 'K':
- nogood = !matchkey(*patarg++); /* attempt the match */
- break;
-
- case '#': /* one of the numeric patterns */
- nogood = !matchnum(*pats++); /* attempt numeric parse */
- break;
-
- case 'p': /* next char punctuation */
- case 'P':
- if (dtlen <= 0) { /* insufficient input? */
- dtinc = nogood = TRUE; /* then set incomplete failure */
- dtcmp = " "; /* complete with a space */
- }
- else {
- tc = *dttext; /* get next char, do not consume it */
- if (isalnum(tc)) /* not a punctuation char? */
- nogood = TRUE; /* then signal failure */
- else
- set1val((int) tc); /* return ASCII code of char */
- }
- break;
-
- case 'w': /* match a run of whitespace */
- case 'W':
- if (dtlen <= 0) { /* insufficient chars? */
- dtinc = nogood = TRUE; /* signal incomplete failure */
- dtcmp = " "; /* complete with a space */
- break;
- }
- tc = *dttext; /* get next input char, unquoted */
- if ((tc != SPACE) && (tc != TAB)) /* first char not whitespace? */
- nogood = TRUE;
- else {
- while ((--dtlen) > 0) { /* loop through nonempty run */
- tc = *(++dttext);
- if ((tc != SPACE) && (tc != TAB))
- break; /* until non-whitespace encountered */
- }
- if (dtlen == 0) /* if exited loop for lack of input */
- dttext++; /* pointer was not bumped enough */
- set1val(0); /* always return zero */
- }
- break;
-
- case 'd': /* match a delimiter char */
- case 'D':
- delims = (char *) *patarg++; /* point to delimiter set */
- #ifdef TRACE
- fprintf(stderr,"Delims [%s] ",delims);
- #endif
- if (dtlen <= 0) { /* no input left? */
- nogood = dtinc = TRUE; /* signal incomplete failure */
- dtcmp = NULL; /* no completion */
- }
- else {
- nogood = TRUE; /* assume first char not delimiter */
- tc = *dttext++; /* get next input char */
- dtlen--; /* and count it */
- for (i = 0; delims[i] != NULCHAR; i++) /* loop through delimiters */
- if (delims[i] == tc) { /* found a match? */
- nogood = FALSE; /* signal success */
- set1val((int) i); /* return index of matching delim */
- break;
- }
- }
- break;
-
- case 'r': /* recursively invoke a pattern set */
- case 'R':
- #ifdef TRACE
- trnl();
- #endif
- nogood = !match(*patarg++,flags); /* invoke the pattern matcher */
- #ifdef TRACE
- trindent(); fprintf(stderr,"Result for %r: ");
- #endif
- break;
-
- case 'f': /* invoke a function */
- case 'F':
- nogood = !(*((int (*)()) *patarg++))(&i); /* get return value in i */
- if (!nogood)
- set1val(i); /* set return value on success */
- break;
-
- case 'z': /* store a zero value */
- case 'Z':
- *(*patarg++) = 0; /* set the value, move to next arg */
- break;
-
- case 'n': /* store a -1 value */
- case 'N':
- *(*patarg++) = -1; /* set the value, move to next arg */
- break;
-
- case '+': /* skip next argument */
- patarg++; /* bump the pointer */
- break;
-
- default: /* anything else is an error */
- nogood = TRUE;
- break;
- }
-
- if (dtinc) /* incomplete parse? */
- #ifdef TRACE
- { fprintf(stderr,"INC\n");
- #endif
- return(FALSE); /* yes, fail immediately */
- #ifdef TRACE
- }
- #endif
-
- if (nogood) /* pattern element failed? */
- if (opt) { /* optional element? */
- #ifdef TRACE
- fprintf(stderr,"NO (OK)\n");
- #endif
- clearval(); /* yes, all values to -1 */
- dttext = opttext; /* restore input pointers */
- dtlen = optlen;
- }
- else
- #ifdef TRACE
- { fprintf(stderr,"NO\n");
- #endif
- return(FALSE); /* fail if failing elt not optional */
- #ifdef TRACE
- }
- else
- fprintf(stderr,"OK\n");
- #endif
-
- }
- return(TRUE); /* got through entire pattern */
- }
-
-
-
- /* matchkey
- **
- ** Purpose:
- ** Implement the %k special pattern element. Input characters are matched
- ** against the keywords in the given array until a matching key is located,
- ** or until they have all failed to match. In the former case, the index
- ** of the matching keyword is set into the patval array as the value
- ** returned by this pattern element, and TRUE is returned. In the latter
- ** case, FALSE is returned. If matching fails on any keyword as a result
- ** of insufficient input, dtinc is set and an immediate failure is given.
- ** A keyword matches the input only if the input contains all the characters
- ** contained in the keyword up to a vertical bar in the keyword. After
- ** that, additional matching characters are consumed as well, up to the
- ** first nonmatching character or the end of the keyword. Case is ignored
- ** when matching letters.
- **
- ** In the case that the end of the input is reached (causing dtinc to be
- ** set), if the current input uniquely matches exactly one of the keywords,
- ** the remainder of that keyword is set as the completion text, even if
- ** the input has not reached the vertical bar marker for that keyword.
- ** Input arguments:
- ** keys - Pointer to an array of keyword strings, terminated by NULL.
- **
- ** Output arguments: None.
- ** Returns: TRUE for success, FALSE for failure or incomplete.
- **/
-
-
- static int
- matchkey(keys)
- char **keys;
- {
- int len; /* length of individual key match */
- char *key; /* pointer to individual key */
- int sawbar; /* TRUE if bar seen in matching key */
- int i,j,k;
- #define KEYCMPLEN 15 /* size of following buffer */
- static char keycmp[KEYCMPLEN]; /* keyword completion text buffer */
-
- #ifdef TRACE
- fprintf(stderr,"Keys [%s...] ",keys[0]);
- #endif
- if (dtlen == 0) { /* no input to match? */
- dtinc = TRUE; /* set incomplete flag */
- dtcmp = NULL; /* and no completion */
- return(FALSE); /* and fail immediately */
- }
-
- for (i = 0; (key = keys[i]) != NULL; i++) { /* loop through the keys */
- len = matchk1(dttext,dtlen,key,&sawbar); /* try to match current key */
- if (dtinc || sawbar)
- break; /* exit on success or end of input */
- }
-
- if (dtinc) { /* ran out of input? */
- key += len; /* point to remainder of keyword */
- if (sawbar) key++; /* account for bar if seen */
- for (j = k = 0; k < KEYCMPLEN-1; j++) /* copy it to our buffer */
- if (key[j] == '|')
- continue; /* skip the bar */
- else if (key[j] == NULCHAR)
- break; /* exit at end of key */
- else
- keycmp[k++] = key[j]; /* copy other chars */
- keycmp[k] = NULCHAR; /* tie off completion buffer */
- dtcmp = keycmp; /* assume this is our completion */
- if (!sawbar) /* if less than minimum match */
- while ((key = keys[++i]) != NULL) /* then look for a 2nd match */
- if (matchk1(dttext,dtlen,key,&sawbar) == len) {
- dtcmp = NULL; /* if found, then just beep */
- break;
- }
- return(FALSE); /* fail the match in any case */
- }
- else if (sawbar) { /* successful match? */
- dttext += len; /* update input pointers */
- dtlen -= len;
- set1val(i); /* set pattern return value */
- return(TRUE); /* and succeed */
- }
- else /* all keys failed outright */
- return(FALSE); /* so just fail */
- }
-
-
-
- /* matchk1 - Aux routine for matchkey
- **
- ** Purpose:
- ** Determine how much of the input text matches a given keyword, and
- ** return the number of matching chars. Set a return flag indicating
- ** whether or not a vertical bar was passed during the match (meaning
- ** that the text should be accepted as matching the keyword).
- **
- ** Input arguments:
- ** text - A pointer to the text to be matched.
- ** textlen - The length of the text to be matched. Guaranteed not
- ** to be zero.
- ** key - A pointer to the null-terminated keyword text.
- **
- ** Output arguments:
- ** sawbar - Set TRUE if a vertical bar is passed while matching
- ** the key. Otherwise, FALSE is set.
- **/
-
- static int
- matchk1(text,textlen,key,sawbar)
- char *text,*key;
- int textlen,*sawbar;
- {
- char tc,kc; /* individual chars to compare */
- int len = 0; /* number of chars matched */
-
- *sawbar = FALSE; /* bar not seen yet */
- while (textlen != 0) { /* loop through text */
- tc = *text++; /* get next char */
- textlen--; /* and count it */
- if ((kc = *key++) == NULCHAR)
- break; /* exit at end of key */
- if (kc == '|') {
- *sawbar = TRUE; /* there's the bar! */
- if ((kc = *key++) == NULCHAR) /* move to next char */
- break;
- }
- if (islower(tc)) /* convert to upper case */
- tc = toupper(tc);
- if (islower(kc))
- kc = toupper(kc);
- if (tc != kc) /* chars don't match? */
- return(len); /* return length of match */
- else
- len++; /* count matching chars */
- }
-
- if (kc == '|') /* input just barely met minimum? */
- *sawbar = TRUE; /* yup, set flag */
- else if (kc != NULCHAR) /* reached end of input? */
- dtinc = TRUE; /* set incomplete flag */
- return(len); /* give number of matching chars */
- }
-
-
-
- /* matchnum
- **
- ** Purpose:
- ** Parse a decimal number in the current input, and check the value
- ** according to the number type indicated by the passed character
- ** (one of the characters that can follow "%#" in a pattern).
- ** Set return value(s) according to the type, as well.
- **
- ** Input arguments:
- ** type - Character selecting the treatment required for the parsed number.
- **
- ** Output args: None.
- ** Returns: TRUE if successful parse, FALSE otherwise.
- **/
-
- static int
- matchnum(type)
- char type;
- {
- int val = 0; /* value parsed from data */
- char tc; /* individual chars from input */
- int digcnt = 0; /* number of digits in number */
-
- #ifdef TRACE
- fprintf(stderr,"Type [%c] ",type);
- #endif
- if (dtlen <= 0) { /* no input left? */
- dtinc = TRUE; /* set incomplete flag */
- dtcmp = NULL; /* no completion text */
- return(FALSE); /* and fail */
- }
- tc = *dttext; /* get first char */
- if (!isdigit(tc))
- return(FALSE); /* no digits to parse */
- while (isdigit(tc)) { /* loop through digits */
- val = (val*10) + tc - '0'; /* add digits into value */
- digcnt++; /* and count them */
- tc = *(++dttext); /* grab the next character */
- if ((--dtlen) <= 0) /* and count the last one */
- break; /* exit loop if no more chars */
- }
-
- switch(type) { /* now validate the number */
- case 'm': /* month number, range 1-12 */
- if ((digcnt > 2) || (val < 1) || (val > 12))
- return(FALSE); /* out of range */
- val--; /* drop to zero origin */
- break;
- case 'd': /* day number, range 1-31 */
- if ((digcnt > 2) || (val < 1) || (val > 31))
- return(FALSE); /* out of range */
- val--; /* drop to zero origin */
- break;
- case 'y': /* 2-digit year number */
- if ((digcnt != 2) || (val > 99)) /* value out of range? */
- return(FALSE);
- break; /* leave good value as is */
- case 'Y': /* 4-digit year number */
- if ((digcnt < 3) || (digcnt > 4) || (val > 9999))
- return(FALSE); /* out of range */
- break; /* leave good value as is */
- case 'h': /* hour in range 1-12 */
- if ((digcnt > 2) || (val < 1) || (val > 12))
- return(FALSE); /* out of range */
- break; /* no modificaton to value */
- case 'H': /* hour in range 0-24 */
- if ((digcnt > 2) || (val > 24))
- return(FALSE); /* out of range */
- break; /* no modification to value */
- case 'M': /* 4-digit hours-and-minutes */
- if ((digcnt < 3) || (digcnt > 4) || (val > 2400))
- return(FALSE); /* out of range */
- if ((val % 100) > 59)
- return(FALSE); /* minutes out of range */
- set2val(val/100,val%100); /* break up the input */
- return(TRUE); /* and succeed */
- case 's': /* seconds or minutes, range 0-59 */
- if ((digcnt != 2) || (val > 59))
- return(FALSE); /* out of range */
- break; /* good value is left as is */
- default: /* unknown type */
- return(FALSE); /* just fail */
- }
-
- set1val(val); /* set return value */
- return(TRUE); /* and succeed */
- }
-
-
-
- /* Routines for manipulating return value array:
- ** clearval - Set all values to -1.
- ** set1val - Set first value, clear the rest to -1.
- ** set2val - Set first two values, clear the rest to -1.
- **
- ** Input arguments:
- ** val1 - First value to set (set1val and set2val only)
- ** val2 - Second value to set (set2val only)
- **
- ** Output arguments: None.
- ** Returns: Nothing.
- **/
-
- static
- clearval()
- {
- int i;
- for (i = 0; i < MAXVAL; i++) /* loop through value array */
- patval[i] = -1; /* and clear the entries */
- }
-
- static
- set1val(val1)
- int val1;
- {
- int i;
- for (i = 1; i < MAXVAL; i++) /* clear all but first entry */
- patval[i] = -1;
- patval[0] = val1; /* and set given value */
- }
-
- static
- set2val(val1,val2)
- int val1,val2;
- {
- int i;
- for (i = 2; i < MAXVAL; i++) /* clear all but first two entries */
- patval[i] = -1;
- patval[0] = val1; /* and set given values */
- patval[1] = val2;
- }
-
-
-
-
- #ifdef TRACE
-
- /* Routines to print out trace information during the pattern match */
-
- static
- trindent() /* indent to current trace level */
- {
- int i;
- for (i = 0; i < tracelev; i++)
- fprintf(stderr," ");
- }
-
- static
- trshow() /* show remaining unparsed input */
- {
- int i;
- char c;
- for (i = 0; i < dtlen; i++) {
- c = dt[i];
- if ((c == '\177') || (c < SPACE))
- fprintf(stderr,"^%c",c^'\100');
- else
- fprintf(stderr,"%c",c);
- }
- }
-
- static
- trargs() /* print contents of patval array */
- {
- int i;
- for (i = 0; i < MAXVAL; i++) {
- if (i != 0)
- fprintf(stderr," ");
- fprintf(stderr,"%d",patval[i]);
- }
- }
-
- static
- trnl() /* print a newline */
- {
- fprintf(stderr,"\n");
- }
-
- #endif
-